

Excel2007で、VBAを利用した簡単なデータエントリ、管理ソフトを作成しています。
ACCESSが無いため、データベースもExcelファイルを使用しています。
ADODBで、データベース用のExcelファイルを開くのですが、エントリ数が増えるに従い、openに時間がかかるようになってきました。そのため、プログレスバーで、VBAが動作していることをアピールすることとしました。
まず、非同期接続を試したのですが、connectionを数回OpenとCloseを繰り返すと、coinitializeでエラーが出てしまい、Excelが落ちる状況となってしまうためあきらめました。
次の手段として、CreateThreadでスレッドを作成して、connectionOpenのスレッドと、プログレスバーのコントロールを分離しようと作成してみましたが、CreateThreadで作成した方のプログラムがうまいこと動作してくれません。
ConnectionOpenをメイン、プログレスバーを別スレッドにしたもの、プログレスバーをメイン、ConnectionOpenを別スレッドにしたものを両方作成してみましたが、どちらも別スレッドにした方がうまく動きません。
debug.print "test"を別スレッドの1行目に入れたところ、イミディエイトに表示されるので、処理が渡っていないわけではないようです。
また、openをメインスレッドにした時にわかっているのは、メインスレッドのADOCon.Openの行が実行されたと同時に、別スレッドが止まってしまっているようです。
もしかして、CreateThreadは割り込みがかけられないような状況では別のスレッドは動作しないのでしょうか?また、CreateThreadで作成されたスレッドは、重たい処理は無理なのでしょうか?
テスト用のデータです。
'Busyというユーザーフォームに、PBerというプログレスバーを配置
'C:\Users\xx\Desktop\に、DBファイルを配置 XXは、ユーザー名
'mihon.xlsxは、約5MB
'変数等は、両タイプとも共通
Public bRun As Boolean
Public adoCON As New ADODB.Connection
Public Declare Function CreateThread Lib "kernel32" (ByVal lpThreadAttributes As Long, _
ByVal dwStackSize As Long, ByVal lpStartAddress As Long, _
ByRef lpParameter As Long, ByVal dwCreationFlags As Long, _
ByRef lpThreadID As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
'connectionOpenをメイン、プログレスバーを別スレッド
Sub AdoOpen()
Dim ThreadId As Long
Dim hThread As Long
With Busy
.BusyMes.Caption = "DB接続処理中"
.PBar.Visible = True
.PBar.Value = 0
.PBar.Min = 0
.PBar.Max = 10
.Show vbModeless
End With
DoEvents
bRun = False
hThread = CreateThread(0&, 0&, AddressOf Counter, 0&, 0&, ThreadId)
Application.Wait [NOW()+"0:00:00.5"]
With adoCON
.Provider = "Microsoft.ACE.OLEDB.12.0"
.Properties("Extended Properties") = "Excel 12.0"
.Open "C:\Users\xx\Desktop\mihon.xlsx"
End With
bRun = True
If hThread Then
CloseHandle hThread
hThread = 0
End If
With Busy
.BusyMes.Caption = ""
.PBar.Value = 0
.PBar.Visible = False
.Hide
End With
DoEvents
End Sub
Function Counter() ' As Boolean
Dim bCountup As Boolean
Do Until bRun
Select Case Busy.PBar.Value
Case 0
bCountup = True
Case 10
bCountup = False
End Select
If bCountup Then
Busy.PBar.Value = Busy.PBar.Value + 1
Else
Busy.PBar.Value = Busy.PBar.Value - 1
End If
Sleep 500
Loop
End Function
'プログレスバーをメイン、connectionOpenを別スレッド
Sub CounterStart()
Dim bCountup As Boolean
Dim ThreadId As Long
Dim hThread As Long 'スレッドハンドル
With Busy
.BusyMes.Caption = "DB接続処理中"
.PBar.Visible = True
.PBar.Value = 0
.PBar.Min = 0
.PBar.Max = 10
.Show vbModeless
End With
DoEvents
bRun = False
hThread = CreateThread(0&, 0&, AddressOf Counter2, 0&, 0&, ThreadId)
Do Until bRun
Select Case Busy.PBar.Value
Case 0
bCountup = True
Case 10
bCountup = False
End Select
If bCountup Then
Busy.PBar.Value = Busy.PBar.Value + 1
Else
Busy.PBar.Value = Busy.PBar.Value - 1
End If
Application.Wait [NOW()+"0:00:01.5"]
Loop
If hThread Then
CloseHandle hThread
hThread = 0
End If
With Busy
.BusyMes.Caption = ""
.PBar.Value = 0
.PBar.Visible = False
.Hide
End With
DoEvents
End Sub
Function Counter2()
With adoCON
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=" & ObjDB.Value & "; Extended Properties=""Excel 12.0;"""
.Open "C:\Users\xx\Desktop\mihon.xlsx"
End With
bRun = True
End Function
No.2ベストアンサー
- 回答日時:
> ACCESSが無いため、データベースもExcelファイルを使用しています。
ACCESSが無くてもMDBファイルをデータベースとして扱えますよ。
データ量が多いなら、MDBファイルを使うことを検討してはいかがでしょう。
返信が遅くなりました。
結局、Excelファイルをデータベースファイルとして利用することをあきらめました。
ExcelでMDBを作成し、今までのデータをインポートして利用することができました。
adoで作成していたため、変更箇所も少なく、スムーズに移行しました。
ありがとうございました。
No.1
- 回答日時:
先ずCreateThreadで指定するメソッドは
整数のパラメータを1個持ち、整数の値を
返すpascal型でなければなりません。
VBAの関数はpascal型なので無意識でも
よいのですが、掲題のCounter2はパラ
メータが無く、戻り値がVARIANT型です。
VARIANT型を返す関数は内部的には
戻り値ではなく、値をセットするポインタを
パラメータに持つので、形式はたまたま合い
ますが、「運よく動いている」だけであり、
好ましい状態ではありません。
VBAおよびオブジェクトの大半はスレッド
セーフではありません。よって、VBAを使う
マルチスレッドは実行不可能といってよいと
思います。
C言語でDLLを作り、その中でマルチスレッドに
することはできます。但し、オブジェクトを
スレッド間で使うのはできないと思った方が
よいでしょう。スレッド内でオブジェクトの
インスタンス化~解放までするなら使用でき
ます。しかし、C言語でCOMオブジェクトを扱う
のはとても面倒で、VBでやるようなものとは
ワケが違います。
MySQLやPostgreSQLなどを使ってDBを構築し、
マルチプロセスでデータをロードすることを
考えてみてはいかがでしょうか。
この回答への補足
nda23様
早速の回答ありがとうございます。
やはり、VBAでマルチスレッドは難しいのですね。
MySQLなどのフリーソフトや自作Cなどは、会社のセキュリティポリシーの関係で自由な取り扱いができないんです。
業務に必要だからと本社のシステムグループに使用許可をもらうにしても、他のインストール済みソフト(業務用の基幹システム等)への影響や、複数のソフトの組み合わせによる著作権等への影響がないか、あらゆるチェックをしなければ許可は出せないそうです。(実際には影響範囲がチェックしきれないため許可はでない)
そのため、VBAであれば前述の問題が発生しないため、VBAのみでどこまでできるかに執着していた次第です。
CounterStartから、Counter2を別スレッドで呼び出すのは、戻り値が整数値ではないためだめなのですね。まぁ、connectionを開くのだからそうですよね。
実際、プログレスバーが動いてもconnectionが開かないので、Counter2が止まっているのは間違いないと思います。
AdoOpenから、Counterを別スレッドで呼び出す方のは、戻り値がVARIANT型ではないですけど、やはり難しいのでしょうか。
ネットで調べた中に、VBですが、フォームに配置したラベルのバックカラーを別スレッドで変更するっていうのがあって、それはExcelVBAでも問題なく動きました。
しかし、質問に記載したもの(フォームに配置したプログレスバーを別スレッドで操作する)を実行すると、メインスレッドのADOCon.openまで来ると、open処理が終了するまで別スレッドに処理がいかないようなんですが。
Excelファイルでデータベースはあきらめました。
他の方の意見を参考に、MDBファイルに移行することにどうにか成功しました。
ありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Visual Basic(VBA) VBAでoutlook365が起動しません。 4 2022/08/25 13:31
- Visual Basic(VBA) VBAが止まります。 3 2022/08/31 14:09
- Excel(エクセル) 2つのVBAを一緒にしたら機能しなくなりました(エクセル) 7 2022/06/02 12:41
- Visual Basic(VBA) 動きっぱなしです。止め方とプロシージャの間違いを教えて下さい! 5 2022/08/15 23:08
- Visual Basic(VBA) 別シートから年齢別の件数をカウントしたいの続き 5 2023/01/24 00:16
- Visual Basic(VBA) いつもお世話になっております、VBAで教えて頂きたいのですが 2 2022/05/05 22:20
- Visual Basic(VBA) VBA 別ブックからの転記の高速化について VBA 別ブックからの転記の高速化についてご教授下さい。 19 2022/07/26 13:07
- Visual Basic(VBA) countifsについての質問 3 2023/03/08 13:45
- Excel(エクセル) B列に文字がはいったらA列に数字が入るマクロードを完成させたい 4 2023/04/21 01:58
- Visual Basic(VBA) Excel VBAの解読について質問があります。 概要は、マクロでチェックボックスにチェックすると日 1 2023/02/10 07:50
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
MFCスレッド CriticalSection
-
スレッドにて同一メモリの書き...
-
スレッドの監視方法
-
スレッド終了を待つ間に開放さ...
-
Macターミナルで実行中のプログ...
-
CGIの記述について教えてくださ...
-
PerlScriptでMSAgentをプログラ...
-
winsockを使った通信方法
-
テキストファイルを読み込んで...
-
マイコンからプログラムを読み...
-
家電製品の電力周波数を変える機械
-
VBAの配列サイズとメモリに関して
-
ロータリーエンコーダがうまく...
-
メモリ解析の方法
-
アセンブリ言語の問題
-
MACアドレスの調べ方
-
PC-98で拡張メモリを使え...
-
プロセスIDからウィンドウハ...
-
C言語でチャットプログラミング...
-
SSL_connect(ssl)
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
スレッドの監視方法について
-
VC++スレッドの正しい終了のさ...
-
スレッドにて同一メモリの書き...
-
スレッドの終了の仕方
-
VB2005 シリアル通信のClose処理
-
WaitForSingleObjectの使い方に...
-
別スレッドのデータを受信できない
-
同一スレッドで、ロックをかけ...
-
CWnd::OnTimerのスレッドの取得
-
スレッドの安全な終了のさせ方
-
メインダイアログが最背面に表...
-
スレッド一覧の取得
-
Windows上で、シグナル(SIGTERM...
-
C言語で一定時間待機後、再実行
-
C#でスレッド実行中のイベント...
-
複数スレッドを動作させるのに...
-
.netアプリへのSendMessageでフ...
-
DirectX LPDIRECT3DDEVICE9のマ...
-
スレッド内でコントロールやWin...
-
DirectX 11のConsntanBuffer
おすすめ情報